mod frag_shader;
mod matrix;
mod rasterizer;
mod texture;
mod triangle;
use crate::frag_shader::texture_shader;
use frag_shader::normal_shader;
use image::io::Reader as ImageReader;
use rasterizer::Rasterizer;
use texture::Texture;

use matrix::{Mat4f, Vec2f, Vec3f, Vec3i, Vector};
#[derive(Clone, Copy)]
pub enum Fragment {
    Triangle,
}
fn main_loop() {
    // spot_texture.png
    // let model_name = "hmap.jpg";
    let img_file = ImageReader::open(concat!("static/spot/", "spot_texture.png"))
        .expect("Failed to load texture file")
        .decode()
        .expect("Failed to decode");
    // let img_buf = img_file.as_rgb8().expect("load rgb8 failed");
    // img_buf.get_pixel(1, 2).0;
    let mut r = Rasterizer::new(700, 700, texture_shader);
    // let mut r = Rasterizer::new(700, 700, normal_shader);
    // NOTE: 左手坐标系和右手坐标系
    r.texture_img = Some(Texture::new(img_file));
    r.set_project(Mat4f::perspective(
        45_f32.to_radians(),
        r.aspect_ratio(),
        0.1,
        50.0,
    )); // TODO 这里和示范的不一样 投影矩阵是否推错？ -140角度下尾部膨胀扭曲
        // TODO 纹理边缘有缝隙与锯齿？
    r.set_view(Mat4f::view(
        -Vec3f::unit_z() * 10.0,
        Vec3f::unit_z(),
        Vec3f::unit_y(),
    )); // 140 顺时针 逆时针似乎不同
    r.set_model(Mat4f::rotate(Vec3f::unit_y(), 140_f32.to_radians()) * Mat4f::scale(2.5));
    // TODO 没有做裁剪 会扭曲 怀疑插值 与重心坐标
    // GPU_LOAD_OPTIONS.single_index = true; on default
    let (models, _materials) = tobj::load_obj(
        "static/spot/spot_triangulated_good.obj",
        &tobj::GPU_LOAD_OPTIONS,
    )
    .expect("Failed to load OBJ file");

    // Materials might report a separate loading error if the MTL file wasn't found.
    // If you don't need the materials, you can generate a default here and use that
    // instead.
    // let materials = materials.expect("Failed to load MTL file");
    for model in models {
        let mesh = &model.mesh;
        let pos: Vec<Vec3f> = mesh
            .positions
            .iter()
            .enumerate()
            .step_by(3)
            .map(|(i, ..)| {
                Vec3f::from([
                    mesh.positions[i],
                    mesh.positions[i + 1],
                    mesh.positions[i + 2],
                ])
            })
            .collect();
        let col = vec![Vec3f::from([217.0, 238.0, 185.0]); pos.len().clone()];
        let pos_id = r.load_positions(pos);
        let ind = mesh
            .indices
            .iter()
            .enumerate()
            .step_by(3)
            .map(|(i, ..)| {
                Vec3i::from([
                    mesh.indices[i] as isize,
                    mesh.indices[i + 1] as isize,
                    mesh.indices[i + 2] as isize,
                ])
            })
            .collect();
        let ind_id = r.load_indices(ind);
        let texcoor: Vec<Vec2f> = mesh
            .texcoords
            .iter()
            .enumerate()
            .step_by(2)
            .map(|(i, ..)| Vec2f::from([mesh.texcoords[i], mesh.texcoords[i + 1]]))
            .collect();
        let tex_id = r.load_texcoor(texcoor);
        let norm: Vec<Vec3f> = mesh
            .normals
            .iter()
            .enumerate()
            .step_by(3)
            .map(|(i, ..)| Vec3f::from([mesh.normals[i], mesh.normals[i + 1], mesh.normals[i + 2]]))
            .collect();
        let nor_id = r.load_norm(norm);
        let col_id = r.load_colors(col);
        r.inds.push(AttrInds {
            pos_id,
            ind_id,
            col_id,
            tex_id,
            nor_id,
            ..Default::default()
        });
        // r.draw();
        r.run();
    }
}
#[test]
fn demo2_loop() {
    use crate::frag_shader::color_shader;
    let mut r = Rasterizer::new(700, 700, color_shader);
    r.set_project(Mat4f::perspective(
        45_f32.to_radians(),
        r.aspect_ratio(),
        0.1,
        50.0,
    ));
    r.set_view(Mat4f::view(
        Vec3f::unit_z() * 5.0,
        -Vec3f::unit_z(),
        Vec3f::unit_y(),
    ));
    r.set_model(Mat4f::rotate(Vec3f::unit_z(), 0_f32.to_radians()));
    let pos_id = r.load_positions(vec![
        Vec3f::from([2., 0., -2.]),
        Vec3f::from([0., 2., -2.]),
        Vec3f::from([-2., 0., -2.]),
        Vec3f::from([3.5, -1., -5.]),
        Vec3f::from([2.5, 1.5, -5.]),
        Vec3f::from([-1., 0.5, -5.]),
    ]);
    let ind_id = r.load_indices(vec![[0, 1, 2].into(), [3, 4, 5].into()]);
    let col_id = r.load_colors(vec![
        [217.0, 238.0, 185.0].into(),
        [217.0, 238.0, 185.0].into(),
        [217.0, 238.0, 185.0].into(),
        [185.0, 217.0, 238.0].into(),
        [185.0, 217.0, 238.0].into(),
        [185.0, 217.0, 238.0].into(),
    ]);
    r.draw(AttrInds {
        pos_id,
        ind_id,
        col_id,
        ..Default::default()
    });
    r.run();
}
fn main() {
    // test_loop();
    main_loop();
}

fn test_loop() {
    use crate::frag_shader::color_shader;
    let mut r = Rasterizer::new(700, 700, color_shader);
    r.set_project(Mat4f::perspective(
        45_f32.to_radians(),
        r.aspect_ratio(),
        0.1,
        50.0,
    ));
    r.set_view(Mat4f::view(
        Vec3f::unit_z() * 5.0,
        -Vec3f::unit_z(),
        Vec3f::unit_y(),
    ));
    r.set_model(Mat4f::rotate(Vec3f::unit_z(), 0_f32.to_radians()));
    let pos_id = r.load_positions(vec![
        Vec3f::from([2., 0., -2.]),
        Vec3f::from([0., 2., -2.]),
        Vec3f::from([-2., 0., -2.]),
        Vec3f::from([2., 0., -2.]),
        Vec3f::from([0., -2., -2.]),
        Vec3f::from([-2., 0., -2.]),
    ]);
    let ind_id = r.load_indices(vec![[0, 1, 2].into(), [3, 4, 5].into()]);
    let col_id = r.load_colors(vec![
        [217.0, 238.0, 185.0].into(),
        [217.0, 238.0, 185.0].into(),
        [217.0, 238.0, 185.0].into(),
        [185.0, 217.0, 238.0].into(),
        [185.0, 217.0, 238.0].into(),
        [185.0, 217.0, 238.0].into(),
    ]);
    r.draw(AttrInds {
        pos_id,
        ind_id,
        col_id,
        ..Default::default()
    });
    r.run();
}
#[test]
fn persp() {
    use matrix::Vec4f;
    let p = Mat4f::perspective(45_f32.to_radians(), 1.0, 0.1, 50.0);
    let v = p * Vec4f::from([0., 0., -50., 1.]);
    assert_eq!(v[2] / v[3], -1.0)
}

#[derive(Clone, Copy)]
pub struct AttrInds {
    frag_type: Fragment,
    pos_id: usize,
    ind_id: usize,
    col_id: usize,
    tex_id: usize,
    nor_id: usize,
}

impl Default for AttrInds {
    fn default() -> Self {
        Self {
            pos_id: 0,
            ind_id: 0,
            col_id: 0,
            tex_id: 0,
            nor_id: 0,
            frag_type: Fragment::Triangle,
        }
    }
}
